/*
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @brief the main source for chinese segment function for postgresql
 *
 *  Please Read INSTALL first
 *
 *  Our Project Page : http://code.google.com/p/solor/
 *
 *  changelog 
 *  ------------------------------------
 *  - Jan 27 2008  :
 *      - fix iconv return zero-length char bug
 *      - add \0 to the end of return string
 *      - add scws_get_result loop to get all result 
 *  - Jan 26 2008  : 
 *  -   - initial version
 *  -   - need libiconv
 *  ------------------------------------
 *
 * @author jingxu <rainx1982@gmail.com>
 * @version 0.0.1
 */


#include <iconv.h>
#include "postgres.h"  // needed by postgresql 
#include "fmgr.h"
#include "scws.h"      // get chinese segmentation support

#define SOLOR_SCWS_DICT_FILE    "/home/solor/conf/scws/solor.xdb" // dict file path
#define SOLOR_SCWS_INI_FILE     "/home/solor/conf/scws/solor.ini" // ini config file path

#define SOLOR_MAX_GBK_BUFFER    1024 * 50  // 50kb
#define SOLOR_MAX_UTF8_BUFFER   1024 * 150  // 150kb

#define SOLOR_DEBUG 0
#define SOLOR_LOG(log) \
    if (SOLOR_DEBUG) printf("%s\n", log);

// Needed by PostgreSql 8.2 + 
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif


/// scws object 
scws_t s;

// input buffer
char sr_in[SOLOR_MAX_GBK_BUFFER];

// output buffer
char sr_out[SOLOR_MAX_UTF8_BUFFER];
char sr_out_utf8[SOLOR_MAX_UTF8_BUFFER];

// iconv handler 
iconv_t sr_iconv_in;
iconv_t sr_iconv_out;


/**
 * @brief initialize scws on the lib first loaded into postgres 
 */
void _PG_init()
{
   // create object
   if (!(s = scws_new())) 
   {
      printf("error, can't init the scws_t!\n");
      exit(-1);
   }
   scws_set_charset(s, "gbk");
   // load dict
   scws_set_dict(s, SOLOR_SCWS_DICT_FILE, SCWS_XDICT_XDB);
   scws_set_rule(s, SOLOR_SCWS_INI_FILE);

   // init handers
   sr_iconv_in = iconv_open("GBK", "UTF-8");
   sr_iconv_out = iconv_open("UTF-8", "GBK");

   SOLOR_LOG("init finish");
}

/**
 * @brief get the segment result
 * @param the text provided to be segmented
 * @returns all segmented substring separated by 'blank'
 */
text* solor_zhseg(text* source)
{
    long len = 0;
    scws_res_t res, cur;
    char* data = (char*) VARDATA(source);
    text* result ;
    size_t in_buf_len = SOLOR_MAX_GBK_BUFFER;
    size_t raw_data_len = 0 ;
    size_t out_utf8_len = SOLOR_MAX_UTF8_BUFFER ;
    size_t out_gbk_len ;

    char* tmp_out;
    char* tmp_in;

    sr_in[0] = 0;
    sr_out[0] = 0;
    sr_out_utf8[0] = 0;
    memset(sr_in, 0, SOLOR_MAX_GBK_BUFFER);
    memset(sr_out, 0, SOLOR_MAX_UTF8_BUFFER);
    memset(sr_out_utf8, 0, SOLOR_MAX_UTF8_BUFFER);

    // get min length between max buffersize and raw text length
    raw_data_len = ((VARSIZE(source) - VARHDRSZ) < SOLOR_MAX_GBK_BUFFER )? (VARSIZE(source) - VARHDRSZ) : SOLOR_MAX_GBK_BUFFER;

    size_t sr_tmp_insize = VARSIZE(source) - VARHDRSZ;
    char* sr_tmp_p    = data;

    *(sr_tmp_p + sr_tmp_insize) = '\0';
    SOLOR_LOG(sr_tmp_p);

    tmp_out = sr_in;
    tmp_in  = sr_tmp_p;

    iconv(  sr_iconv_in, 
            (const char**)&sr_tmp_p, 
            (size_t*) &sr_tmp_insize,
            (char**)  &tmp_out,
            (size_t*) &in_buf_len);

    SOLOR_LOG("I am here");
    SOLOR_LOG(sr_in);

    scws_send_text(s, sr_in, strlen(sr_in));
    
    SOLOR_LOG("just send text");

    // scan to get the length and build the data
    //
    if(res = cur = scws_get_result(s))
    {
        SOLOR_LOG("I am in loop");

        int offset = 0;
        // construct result data
        while (cur != NULL)
        {
          while (cur != NULL)
          {
              // add the word
              memcpy ( sr_out + offset, // new data
                      sr_in + cur->off,
                      cur->len
                     );
              offset += cur->len;
              // add a 'blank'
              memcpy ( (sr_out + offset), 
                      " ",
                      1);
              offset +=1;
              cur = cur->next;
          }
          cur = scws_get_result(s);
        }
        // end up the string
        *(sr_out + offset) = '\0';
        out_gbk_len = strlen(sr_out);
        tmp_in = sr_out;

        SOLOR_LOG(sr_out);

        tmp_out = sr_out_utf8;
        iconv(  sr_iconv_out, 
                (const char**)  &tmp_in, 
                (size_t*) &out_gbk_len , 
                (char**)  &tmp_out, 
                (size_t*)  &out_utf8_len);

        SOLOR_LOG(sr_out_utf8);
        out_utf8_len = strlen(sr_out_utf8);
        result = palloc(out_utf8_len + VARHDRSZ);
        VARATT_SIZEP(result) = out_utf8_len + VARHDRSZ;
        memcpy( VARDATA(result),
                sr_out_utf8,
                out_utf8_len
              );
        *(sr_out_utf8 + out_utf8_len) = '\0'; // add \0 to the end of string 
        scws_free_result(res);
    }
    else
    {
      // build a empty string
      SOLOR_LOG("build a empty string");
      result = palloc(VARHDRSZ + 1);
      VARATT_SIZEP(result) = VARHDRSZ + 1;
      memcpy(VARDATA(result), "", 1);
    }
    return result;
}


/**
 * @brief remove scws object
 */
void _PG_fini()
{
    SOLOR_LOG("fini start");
    if (s) 
    {
      scws_free(s);
    }

    if (sr_iconv_in)
    {
      iconv_close(sr_iconv_in);
    }

    if (sr_iconv_out)
    {
      iconv_close(sr_iconv_out);
    }
    SOLOR_LOG("fini end");
}
